#include "stdafx.h"
#include "Packages.h"
#include "SystemVersion.h"
#include "Logger.h"

#include "Poco/MD5Engine.h"
#include "Poco/DigestStream.h"
#include "Poco/Base64Encoder.h"
#include "Poco/Base64Decoder.h"
#include "Poco/StreamCopier.h"
#include "Poco/File.h"
#include "poco/DateTime.h"
#include "Poco/Exception.h"
#include "Poco/Format.h"
#include "Poco/String.h"
#include "Poco/Any.h"

#include <sstream>
#include <cassert>

std::stringstream debugStream;

namespace ExamSystem
{
    using Poco::DigestEngine;
    using Poco::MD5Engine;
    using Poco::DigestOutputStream;
    using Poco::StreamCopier;
    using Poco::Base64Encoder;
    using Poco::Base64Decoder;
    using Poco::DateTime;
    using Poco::File;
    using Poco::Exception;
    using Poco::Any;
    using Poco::AnyCast;

    typedef CSafeScopedLock StreamLock;

    unsigned int systemID = 0;

#define ThePackageCreator(Type) Type##::packageType, &##Type##::CreatePackage
    
    CPackageManager::CPackageManager(void)
    {
        //RegisterPackage(CKeepAlivePackage::packageType, &CKeepAlivePackage::CreatePackage);
        RegisterPackage(ThePackageCreator(CKeepAlivePackage));
        RegisterPackage(ThePackageCreator(CKeepAliveReplyPackage));
        RegisterPackage(ThePackageCreator(CVersionPackage));
        RegisterPackage(ThePackageCreator(CResultPackage));
        RegisterPackage(ThePackageCreator(CDatabasePackage));
        RegisterPackage(ThePackageCreator(COrderPackage));
        RegisterPackage(ThePackageCreator(CMessagePackage));
        RegisterPackage(ThePackageCreator(CInformationPackage));
        RegisterPackage(ThePackageCreator(CLoginPackage));
        RegisterPackage(ThePackageCreator(CLoginResultPackage));
        RegisterPackage(ThePackageCreator(CRedirectPackage));
        RegisterPackage(ThePackageCreator(CIdentityPackage));
        RegisterPackage(ThePackageCreator(CServerInfoPackage));
        RegisterPackage(ThePackageCreator(CIDAllocPackage));
        RegisterPackage(ThePackageCreator(CConnectionInfoPackage));
        RegisterPackage(ThePackageCreator(CQuestionPackage));
        RegisterPackage(ThePackageCreator(CCriterionPackage));
        RegisterPackage(ThePackageCreator(CQuestionQueryPackage));
        RegisterPackage(ThePackageCreator(CCriterionQueryPackage));
    }

    CPackageManager::~CPackageManager(void)
    {

    }

    SharedPtr<CPackage> CPackageManager::CreatePackage(const std::string &packageType) const
    {
        std::map<std::string, PackageCreator>::const_iterator it = m_packageCreator.find(packageType);
        return it == m_packageCreator.end() ? SharedPtr<CPackage>() : (it->second)();
    }

    void CPackageManager::RegisterPackage(const std::string &packageType, PackageCreator creator)
    {
        m_packageCreator[packageType] = creator;
    }

    CPackageManager & CPackageManager::Manager(void)
    {
        return ms_packageManagerInstance;
    }

    /**************************************************************************\
        Package classes
        \**************************************************************************/
    const std::string CPackage::namespaceURI = "";
    const std::string CPackage::labelPackage = "package";
    const std::string CPackage::labelExamination = "examination";
    const std::string CPackage::attributeType = "type";
    const std::string CPackage::attributeSID = "sid";
    const std::string CPackage::capsuledLabelExamination = "<examination>";
    const std::string CPackage::capsuledLabelExaminationEnd = "</examination>";

#define ImplPackageType(packageClass, typeName) const std::string CPackageBase<##packageClass##>::packageType = #typeName

    //std::string CPackageBase<CKeepAlivePackage>::packageType = "hold";
    ImplPackageType(CKeepAlivePackage, hold);
    ImplPackageType(CKeepAliveReplyPackage, holdReply);
    ImplPackageType(CIdentityPackage, identity);
    ImplPackageType(CVersionPackage, version);
    ImplPackageType(CResultPackage, result);
    ImplPackageType(CDatabasePackage, database);
    ImplPackageType(COrderPackage, order);
    ImplPackageType(CMessagePackage, message);
    ImplPackageType(CInformationPackage, information);
    ImplPackageType(CLoginPackage, login);
    ImplPackageType(CLoginResultPackage, loginResult);
    ImplPackageType(CRedirectPackage, redirect);
    ImplPackageType(CServerInfoPackage, serverInfo);
    ImplPackageType(CIDAllocPackage, idAlloc);
    ImplPackageType(CConnectionInfoPackage, connectionInfo);
    ImplPackageType(CQuestionPackage, question);
    ImplPackageType(CCriterionPackage, criterion);
    ImplPackageType(CQuestionQueryPackage, questionQuery);
    ImplPackageType(CCriterionQueryPackage, criterionQuery);

    // the manager must be defined last;
    CPackageManager CPackageManager::ms_packageManagerInstance;

#define Quated(value) "\"" #value "\""
#define StartWriting \
    if (!pStream) \
        return pStream; \
    SafeSocketOutputStream &stream = *pStream; \
    systemLogger.trace("Sending: " + ToText()); \
    try \
    {\
        StreamLock lock(stream);

#define FinishWriting  \
        stream << std::endl;    \
        stream.flush(); \
    }\
    catch (Exception &exp) \
    {\
        systemLogger.error(exp.displayText());\
    }

#define ParseID \
    if (localName == CPackage::labelPackage) \
    {\
        std::stringstream sstr(attributes.getValue(namespaceURI, attributeSID)); \
        sstr >> m_sourseID; \
    }


    std::string EncodeString(const std::string &str)
    {
        std::stringstream sstr(str);
        std::stringstream sstr2;
        Base64Encoder encoder(sstr2);
        Poco::StreamCopier::copyStream(sstr, encoder);
        encoder.close();

        return sstr2.str();
    }

    std::string DecodeString(const std::string &str)
    {
        std::stringstream sstr(str);
        std::stringstream sstr2;
        Base64Decoder decoder(sstr);
        Poco::StreamCopier::copyStream(decoder, sstr2);
        return sstr2.str();
    }


    /**************************************************************************\
        keep alive package
    \**************************************************************************/
    SharedPtr<SafeSocketOutputStream> & CKeepAlivePackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting            
            stream << "<package sid=\"" << systemID << "\" type=\"hold\" />";
        FinishWriting
        return pStream;
    }

    std::string CKeepAlivePackage::ToText(void) const
    {
        return "KeepAlive.";
    }

    void CKeepAlivePackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
    }

    /**************************************************************************\
        keep alive reply package
    \**************************************************************************/
    SharedPtr<SafeSocketOutputStream> & CKeepAliveReplyPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting 
            stream << "<package sid=\"" << systemID << "\" type=\"holdReply\" />";
        FinishWriting
        return pStream;
    }

    std::string CKeepAliveReplyPackage::ToText(void) const
    {
        return "KeepAliveReply.";
    }

    void CKeepAliveReplyPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
    }

    /**************************************************************************\
        ID distribute package
    \**************************************************************************/
    void CIDAllocPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        std::stringstream sstr(attributes.getValue(namespaceURI, "id"));
        sstr >> m_ID;
    }

    SharedPtr<SafeSocketOutputStream> & CIDAllocPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID 
                << "\" type=" Quated(idAlloc) 
                << " id=\"" << m_ID << "\" />";
        FinishWriting
        return pStream;
    }

    std::string CIDAllocPackage::ToText(void) const
    {
        return Poco::format("Alloc ID: %u", m_ID);
    }

    /**************************************************************************\
        identity package
    \**************************************************************************/
    void CIdentityPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        const std::string &type = attributes.getValue(namespaceURI, "identity");
        if (type == "server")
            m_identity = Server;
        else
            m_identity = Client;
    }

    SharedPtr<SafeSocketOutputStream> & CIdentityPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=" Quated(identity) " identity="
                << (m_identity == Server ? Quated(server) : Quated(client))
                << " />";
        FinishWriting
        return pStream;
    }

    std::string CIdentityPackage::ToText(void) const
    {
        if (m_identity == Server)
            return "Identity: Server.";
        else
            return "Identity: Client.";
    }

    /**************************************************************************\
        version package
    \**************************************************************************/
    void CVersionPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName != "version")
            return;

        const std::string &type = attributes.getValue(namespaceURI, "type");
        if (type == "system")
            m_versionType = System;
        else
            m_versionType = Database;

        m_versionString = attributes.getValue(namespaceURI, "value");
    }

    SharedPtr<SafeSocketOutputStream> & CVersionPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=" Quated(version) ">";
            if (m_versionType == System)
            {
                stream << "<version type=" Quated(system) " value=\"" ExamSystemVersion "\" />";
                debugStream<< "<version type=" Quated(system) " value=\"" ExamSystemVersion "\" />";
            }
            else
            {
                stream << "<version type=" Quated(database) " value=\"" 
                    << m_versionString
                    << "\" />";
            }
            stream << "</package>";
        FinishWriting
        return pStream;
    }

    std::string CVersionPackage::ToText(void) const
    {
        if (m_versionType == System)
            return "System version: " + m_versionString;
        else
            return "Database version: " + m_versionString;
    }

    /**************************************************************************\
        version check package
    \**************************************************************************/
    void CResultPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        const std::string &result = attributes.getValue(namespaceURI, "result");
        if (result == "passed")
            m_result = Passed;
        else
            m_result = Failed;
    }

    SharedPtr<SafeSocketOutputStream> & CResultPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=";
            if (m_result == Passed)
                stream << Quated(result) " result=" Quated(passed) " />";
            else
                stream << Quated(result) " result=" Quated(failed) " />";
        FinishWriting
        return pStream;
    }

    std::string CResultPackage::ToText(void) const
    {
        switch (m_result)
        {
        case Passed:
            return "Result: Passed.";
        case Failed:
            return "Result: Failed.";
        }
        return "";
    }


    /**************************************************************************\
        file package
    \**************************************************************************/
    std::string CFilePackage::ms_receivingPath = "";

    CFilePackage::CFilePackage(void)
        : m_flagCorrect(true)
        , m_flagReceiving(false)
    {

    }

    void CFilePackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        if (localName != "file")
        {
            Reset();
            m_flagCorrect = false;
            return;
        }


        Poco::File recvPath(ms_receivingPath);
        try {
        recvPath.createDirectories();
        } catch (...) {}

        m_fileName = attributes.getValue(CPackage::namespaceURI, "name");
        m_fileName = m_fileName.substr(m_fileName.find_last_of("/\\", m_fileName.size() - 1) + 1);
        m_fileName = ms_receivingPath + m_fileName;
        std::string tryName = m_fileName + ".rcv";
        Poco::File tryFile(tryName);
        int tryCount = 0;
        while (!tryFile.createFile())
        {
            tryCount++;
            std::stringstream sstr;
            sstr << m_fileName << "(" << tryCount << ").rcv";
            tryName = sstr.str();
            tryFile = tryName;
        }
        m_fileName = tryName;

        std::stringstream sstr(attributes.getValue(CPackage::namespaceURI, "size"));
        sstr>>m_fileSize;

        m_fileMd5 = attributes.getValue(CPackage::namespaceURI, "md5");

        if (m_fileName.empty() || m_fileSize <= 0 || m_fileMd5.empty())
        {
            Reset();
            m_flagCorrect = false;
            return;
        }

        // open temp file to receive
        DateTime t;
        char buf[1024];
        sprintf_s(buf, "%s.%d.%d.%d.tmp", m_fileName.c_str(), t.day(), t.second(), t.millisecond());
        m_tempFileName = buf;
        do 
        {
            File file(m_tempFileName);
            if (file.createFile())
                break;
            m_tempFileName += ".tmp";
        } while (m_tempFileName.length() < MAX_PATH);

        m_tempFile.open(m_tempFileName.c_str(), std::ios::binary | std::ios::out | std::ios::in);
        if (!m_tempFile.is_open())
        {
            Reset();
            m_flagCorrect = false;
            return;
        }

        m_flagReceiving = true;
        m_flagCorrect = false;
    }

    void CFilePackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (!m_flagReceiving)
            return;

        m_flagReceiving = false;

        // decode
        Decode();

        // check
        bool check = CheckFile();
        Reset();
        if (!check)
        {
            File file(m_fileName);
            try{
                file.remove();
            } catch (...) {}
            m_flagCorrect = false;
        }
    }

    void CFilePackage::characters(const XMLChar ch[], int start, int length)
    {
        if (!m_flagReceiving)
            return;

        // write received characters to the temp file
        assert(m_tempFile.is_open());
        m_tempFile.write(ch + start, length);            
    }

    void CFilePackage::Reset(void)
    {
        if (m_tempFile.is_open())
            m_tempFile.close();
        File file(m_tempFileName);
        try {
            file.remove();
        } catch (...) {}

        m_tempFileName.clear();
        m_flagReceiving = false;
        m_flagCorrect = true;
    }

    void CFilePackage::Decode(void)
    {
        m_tempFile.seekg(0, std::ios::beg);

        std::ofstream fout(m_fileName.c_str(), std::ios::binary | std::ios::out);
        if (!fout.is_open())
        {
            Reset();
            m_flagCorrect = false;
            return;
        }

        Base64Decoder decoder(m_tempFile);
        StreamCopier::copyStream(decoder, fout);

        fout.close();
    }

    bool CFilePackage::CheckFile(void)
    {
        std::ifstream fin;
        fin.open(m_fileName.c_str(), std::ios::binary);
        if (!fin.is_open())
            return false;

        MD5Engine md5;
        DigestOutputStream dos(md5);
        StreamCopier::copyStream(fin, dos);
        dos.close();
        std::string md5String = DigestEngine::digestToHex(md5.digest());
        fin.seekg(0, fin.end);
        size_t length = fin.tellg();

        if (m_fileSize != (int)length || m_fileMd5 != md5String)
            return false;

        return true;
    }

    SharedPtr<SafeSocketOutputStream> & CFilePackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            // start to write
            stream << "<file name=\""
                << m_fileName
                << "\" size=\""
                << m_fileSize
                << "\" md5=\""
                << m_fileMd5
                << "\">"
                << std::endl;

            Base64Encoder encoder(stream);
            StreamCopier::copyStream(m_fin, encoder);
            encoder.close();

            stream << std::endl
                <<"</file>";
            m_fin.close();
        FinishWriting
        return pStream;
    }

    bool CFilePackage::PrepareSending(void)
    {
        m_fin.open(m_fileName.c_str(), std::ios::binary | std::ios::in);
        if (!m_fin.is_open())
        {
            systemLogger.error("Failed to open file " + m_fileName);
            return false;
        }

        // for debug
        //StreamCopier::copyStream(m_fin, std::cout);
        //m_fin.seekg(0);
        m_fin.seekg(0);
        // get file info
        MD5Engine md5;
        DigestOutputStream dos(md5);
        StreamCopier::copyStream(m_fin, dos);
        dos.close();
        m_fileMd5 = DigestEngine::digestToHex(md5.digest());
        m_fin.seekg(-1);
        m_fileSize = m_fin.tellg();
        m_fin.seekg(0);

        if (m_fileSize == 0)
            return false;

        return true;
    }

    std::string CFilePackage::ToText(void) const
    {
        std::stringstream sstr;
        sstr << "File name: "
            << m_fileName
            << " Size: "
            << m_fileSize
            << " Md5: "
            << m_fileMd5
            << ".";
        return sstr.str();
    }

    /**************************************************************************\
        database package
    \**************************************************************************/
    CDatabasePackage::CDatabasePackage(void)
        : m_flagReceiving(false)
    {
        m_filePackage = new CFilePackage;
    }

    void CDatabasePackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == "file")
        {
            m_flagReceiving = true;
            m_filePackage->startElement(uri, localName, qname, attributes);
        }
    }

    void CDatabasePackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (m_flagReceiving && localName == "file")
        {
            m_filePackage->endElement(uri, localName, qname);
            m_flagReceiving = false;
        }
    }

    void CDatabasePackage::characters(const XMLChar ch[], int start, int length)
    {
        if (m_flagReceiving)
            m_filePackage->characters(ch, start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CDatabasePackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        if (!m_filePackage->PrepareSending())
            return pStream;
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=" Quated(database) ">"<<std::endl;
            m_filePackage->WriteToStream(pStream);
            stream << "</package>";
        FinishWriting
        return pStream;
    }

    bool CDatabasePackage::IsSucceeded(void) const
    {
        if (!m_filePackage)
            false;

        return m_filePackage->m_flagCorrect;
    }

    void CDatabasePackage::SetDatabaseFile(const std::string &fileName)
    {
        if (m_filePackage)
            m_filePackage->m_fileName = fileName;
    }

    const std::string & CDatabasePackage::GetFileName(void) const
    {
        if (m_filePackage)
            return m_filePackage->m_fileName;

        static std::string nullString;
        return nullString;
    }


    std::string CDatabasePackage::ToText(void) const
    {
        return "Database: " + m_filePackage->ToText();
    }

    /**************************************************************************\
        order package
    \**************************************************************************/

    void COrderPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        const std::string &order = attributes.getValue(namespaceURI, "order");
        if (order == "startExam")
            m_order = StartExam;
        else 
            m_order = StopExam;

    }

    SharedPtr<SafeSocketOutputStream> & COrderPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting            
            stream << "<package sid=\"" << systemID << "\" type=" Quated(order) " order="
                << (m_order == StartExam ? Quated(startExam) : Quated(stopExam))
                <<" />";
        FinishWriting
        return pStream;
    }

    std::string COrderPackage::ToText(void) const
    {
        switch (m_order)
        {
        case StartExam:
            return "Exam Order: StartExam.";
        case StopExam:
            return "Exam Order: StopExam.";
        }
        return "";
    }

    /**************************************************************************\
        message package
        \**************************************************************************/
    CMessagePackage::CMessagePackage(void)
        : m_receiving(false)
    {

    }

    void CMessagePackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == "message")
        {
            m_messageBody.clear();
            m_receiving = true;

            const std::string &type = attributes.getValue(namespaceURI, "type");
            if (type == "alert")
                m_messageType = Alert;
            else
                m_messageType = Information;
        }
    }


    void CMessagePackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (localName == "message")
            m_receiving = false;
    }

    void CMessagePackage::characters(const XMLChar ch[], int start, int length)
    {
        if (m_receiving)
            m_messageBody += std::string(ch + start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CMessagePackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting 
            stream << "<package sid=\"" << systemID << "\" type=" Quated(message) ">"
                << "<message type="
                << (m_messageType == Alert ? Quated(alert) : Quated(information))
                << ">"
                << m_messageBody
                << "</message></package>";
        FinishWriting
        return pStream;
    }

    std::string CMessagePackage::ToText(void) const
    {
        switch(m_messageType)
        {
        case Information:
            return "Exam Information: " + m_messageBody;
        case Alert:
            return "Exam Alert: " + m_messageBody;
        }
        return "";
    }


    /**************************************************************************\
        information package
    \**************************************************************************/
    void CInformationPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == labelPackage)
        {
            m_currentType = QuestionTypeCount;
            m_flagReceivingQuestions = false;
            for (int i = 0; i < QuestionTypeCount; i++)
            {
                m_information.examQuestions[i].clear();
                m_information.examQuestionCoefs[i] = 1.0;
            }
        }
        if (localName == "exam")
        {
            m_information.examIdentity = attributes.getValue(namespaceURI, "identity");
            m_information.examDateTime = attributes.getValue(namespaceURI, "datetime");
            const std::string &status = attributes.getValue(namespaceURI, "status");
            if (status == "preparing")
                m_information.examStatus = ExamInformation::Preparing;
            else if (status == "processing")
                m_information.examStatus = ExamInformation::Processing;
            else 
                m_information.examStatus = ExamInformation::Finished;
        }
        else if (localName == "question")
        {
            const std::string &generation = attributes.getValue(namespaceURI, "generation");
            if (generation == "specified")
                m_information.examQuestionGenerationMethod = ExamInformation::Specified;
            else
                m_information.examQuestionGenerationMethod = ExamInformation::Random;

            m_flagReceivingQuestions = true;
        }
        else if (localName == "time")
        {
            const std::string &totalTime = attributes.getValue(namespaceURI, "total");
            const std::string &restTime = attributes.getValue(namespaceURI, "rest");
            std::stringstream sstr(totalTime);
            sstr >> m_information.examTotalTime;
            sstr.clear();
            sstr.str(restTime);
            sstr >> m_information.examRestTime;
        }
        else if (m_flagReceivingQuestions)
        {
            int idx = 0;
            for (; idx < QuestionTypeCount; idx++)
            {
                if (CQuestionPackage::questionTypeName[idx] == localName)
                    break;
            }

            m_currentType = static_cast<QuestionType>(idx);
            if (m_currentType != QuestionTypeCount)
            {
                const std::string &count = attributes.getValue(namespaceURI, "count");
                int n;
                std::stringstream sstr(count);
                sstr >> n;
                if (m_information.examQuestionGenerationMethod == ExamInformation::Specified)
                    m_information.examQuestions[m_currentType].resize(n);
                else
                    m_information.examQuestions[m_currentType].push_back(n);

                const std::string &coef = attributes.getValue(namespaceURI, "scoreCoef");
                sstr.str(coef);
                sstr.clear();
                sstr >> m_information.examQuestionCoefs[m_currentType];
            }
        }
        m_receivingString.clear();
    }

    void CInformationPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (localName == "question")
            m_flagReceivingQuestions = false;
        else if (m_flagReceivingQuestions 
            && m_information.examQuestionGenerationMethod == ExamInformation::Specified
            && m_currentType != QuestionTypeCount)
        {
            std::stringstream sstr(m_receivingString);
            for (size_t i = 0; i < m_information.examQuestions[m_currentType].size(); i++)
                sstr >> m_information.examQuestions[m_currentType][i];
        }
        m_receivingString.clear();
    }

    void CInformationPackage::characters(const XMLChar ch[], int start, int length)
    {
        m_receivingString += std::string(ch + start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CInformationPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting;
        static const std::string status[] = { "preparing", "processing", "finished" };
        static const std::string gen[] = { "random", "specified" };
        stream << Poco::format("<package sid=\"%u\" type=\"information\">", systemID);

        stream << Poco::format("<exam identity=\"%s\" datetime=\"%s\" status=\"%s\" />",
            m_information.examIdentity, m_information.examDateTime, status[m_information.examStatus]
        );

        stream << Poco::format("<question generation=\"%s\">", gen[m_information.examQuestionGenerationMethod]);
        for (int i = 0; i < QuestionTypeCount; i++)
        {
            if (m_information.examQuestionGenerationMethod == ExamInformation::Specified)
            {
                stream << Poco::format("<%s count=\"%u\" scoreCoef=\"%0.1f\">", 
                    CQuestionPackage::questionTypeName[i],
                    m_information.examQuestions[i].size(),
                    m_information.examQuestionCoefs[i]
                    );

                for (size_t j = 0; j < m_information.examQuestions[i].size(); j++)
                    stream << m_information.examQuestions[i][j] << " ";

                stream << Poco::format("</%s>", CQuestionPackage::questionTypeName[i]);
            }
            else
            {
                stream << Poco::format("<%s count=\"%d\"  scoreCoef=\"%0.1f\" />", 
                    CQuestionPackage::questionTypeName[i],
                    m_information.examQuestions[i].empty() ? 0 : m_information.examQuestions[i].front(),
                    m_information.examQuestionCoefs[i]
                    );
            }
        }
        stream << "</question>";

        stream << Poco::format("<time total=\"%u\" rest=\"%u\" />",
            m_information.examTotalTime, m_information.examRestTime
            );

        stream << "</package>";
        FinishWriting;
        return pStream;
    }

    std::string CInformationPackage::ToText(void) const
    {
        std::stringstream sstr;
        static const std::string status[] = { "preparing", "processing", "finished" };
        static const std::string gen[] = { "random", "specified" };
        sstr << "ExamInformation:" << std::endl;

        sstr << Poco::format("identity: %s datetime: %s\nstatus=: %s\n",
            m_information.examIdentity, m_information.examDateTime, status[m_information.examStatus]
        );

        sstr << Poco::format("total time: %u rest time: %u\n",
            m_information.examTotalTime, m_information.examRestTime
            );

        sstr << Poco::format("question generation: %s\n", gen[m_information.examQuestionGenerationMethod]);

        for (int i = 0; i < QuestionTypeCount; i++)
        {
            if (m_information.examQuestionGenerationMethod == ExamInformation::Specified)
            {
                sstr << Poco::format("\t%s[%u, %0.1f]: ", 
                    CQuestionPackage::questionTypeName[i],
                    m_information.examQuestions[i].size(),
                    m_information.examQuestionCoefs[i]
                    );

                for (size_t j = 0; j < m_information.examQuestions[i].size(); j++)
                    sstr << m_information.examQuestions[i][j] << " ";

                sstr << std::endl;
            }
            else
            {
                sstr << Poco::format("\t%s[%d, %0.1f] ", 
                    CQuestionPackage::questionTypeName[i],
                    m_information.examQuestions[i].empty() ? 0 : m_information.examQuestions[i].front(),
                    m_information.examQuestionCoefs[i]
                    );
            }
        }

        return sstr.str();
    }


    /**************************************************************************\
        login package
    \**************************************************************************/
    void CLoginPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        m_currentLabel = localName;
    }

    void CLoginPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (localName == labelPackage)
        {
            //m_username = DecodeString(m_username);
            Poco::trimInPlace(m_username);
            Poco::trimInPlace(m_password);
        }
    }

    void CLoginPackage::characters(const XMLChar ch[], int start, int length)
    {
        if (m_currentLabel == "user")
            m_username += std::string(ch + start, length);
        else if (m_currentLabel == "password")
            m_password += std::string(ch + start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CLoginPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=" Quated(login) ">"
                << "<user>" << /*EncodeString*/(m_username) << "</user>"
                << "<password>" << m_password <<"</password>"
                << "</package>";
        FinishWriting
        return pStream;
    }

    std::string CLoginPackage::ToText(void) const
    {
        return "Login: User: " + m_username + " password: " + m_password + ".";
    }


    /**************************************************************************\
        login result package
    \**************************************************************************/
    void CLoginResultPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        const std::string &result = attributes.getValue(namespaceURI, "result");
        if (result == "succeeded")
            m_result = Succeeded;
        else if (result == "failed")
            m_result = Failed;
        else if (result == "needInvigilator")
            m_result = NeedInvigilator;
        else
            m_result = Error;
    }

    SharedPtr<SafeSocketOutputStream> & CLoginResultPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=" Quated(loginResult) " result=";
            switch (m_result)
            {
            case Succeeded:
                stream << Quated(succeeded);
                break;
            case Failed:
                stream << Quated(failed);
                break;
            case NeedInvigilator:
                stream << Quated(needInvigilator);
                break;
            default:
                stream << Quated(error);
            }
            stream << " />";
        FinishWriting
        return pStream;
    }

    std::string CLoginResultPackage::ToText(void) const
    {
        std::string text = "Login Result: ";
        switch (m_result)
        {
        case Error:
            text += "Error.";
            break;
        case Succeeded:
            text += "Succeeded.";
            break;
        case Failed:
            text += "Failed.";
            break;
        case NeedInvigilator:
            text += "NeedInvigilator.";
            break;
        }
        return text; 
    }


    /**************************************************************************\
        redirection package
    \**************************************************************************/
    void CRedirectPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        m_receivingAddress = false;
        if (localName == labelPackage)
        {
            if (attributes.getValue(namespaceURI, "redirect") == "true")
                m_redirected = true;
            else
                m_redirected = false;
        }
        else if (localName == "target")
        {
            m_receivingAddress = true;
        }
    }

    void CRedirectPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (localName == "target")
            m_receivingAddress = false;
    }

    void CRedirectPackage::characters(const XMLChar ch[], int start, int length)
    {
        m_targetAddress = SocketAddress(std::string(ch + start, length));
    }

    SharedPtr<SafeSocketOutputStream> & CRedirectPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=";
            if (!m_redirected)
            {
                stream << Quated(redirect) " redirect=" Quated(false) " />";
            }
            else
            {
                stream << Quated(redirect) " redirect=" Quated(true) ">"
                    << "<target>"
                    << m_targetAddress.toString()
                    << "</target>"
                    << "</package>";                
            }
        FinishWriting
        return pStream;
    }

    std::string CRedirectPackage::ToText(void) const
    {
        if (m_redirected)
            return "Redirected to: " + m_targetAddress.toString();
        else
            return "No Redirection.";
    }


    /**************************************************************************\
        server information package
    \**************************************************************************/
    void CServerInfoPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == "info")
        {
            std::stringstream sstr(attributes.getValue(namespaceURI, "listeningPort"));
            sstr >> m_port;
        }
    }

    SharedPtr<SafeSocketOutputStream> & CServerInfoPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << "<package sid=\"" << systemID << "\" type=" Quated(serverInfo) ">"
                << "<info listeningPort=\""
                << m_port
                << "\" />"
                << "</package>";
        FinishWriting
        return pStream;
    }

    std::string CServerInfoPackage::ToText(void) const
    {
        std::stringstream sstr;
        sstr<<"Server Info: Port: "<<m_port;
        return sstr.str();
    }


    /**************************************************************************\
        connection information package
    \**************************************************************************/
    void CConnectionInfoPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == labelPackage)
        {
            const std::string &status = attributes.getValue(namespaceURI, "status");
            m_connectionStatus = (status == "connected" ? Connected : Disconnected);
        }
        else if (localName == "server")
        {
            m_serverHostString = attributes.getValue(namespaceURI, "hostString");
            m_sourceIdentity = Server;
        }
        else if (localName == "client")
        {
            m_studentName = attributes.getValue(namespaceURI, "loginName");
            m_studentNumber = attributes.getValue(namespaceURI, "loginNumber");
            m_sourceIdentity = Client;
        }
        else if (localName == "path")
        {
            std::stringstream sstr(attributes.getValue(namespaceURI, "count"));
            int count;
            sstr >> count;
            m_routePath.resize(count);
            m_receivingString.clear();
        }
    }

    void CConnectionInfoPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (localName == "path")
        {
            std::stringstream sstr(m_receivingString);
            for (size_t i = 0; i < m_routePath.size(); i++)
                sstr >> m_routePath[i];
        }
    }

    void CConnectionInfoPackage::characters(const XMLChar ch[], int start, int length)
    {
        m_receivingString += std::string(ch + start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CConnectionInfoPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting
            stream << Poco::format(
                "<package sid=\"%u\" type=%s status=%s>",
                systemID, std::string(Quated(connectionInfo)), m_connectionStatus == Connected ? std::string(Quated(connected)) : std::string(Quated(disconnected))
                );
            if (m_sourceIdentity == Server)
                stream << Poco::format("<server hostString=\"%s\" />", m_serverHostString);
            else
                stream << Poco::format("<client loginName=\"%s\" loginNumber=\"%s\" />", m_studentName, m_studentNumber);

            stream << Poco::format("<path count=\"%u\">", m_routePath.size());
            if (!m_routePath.empty())
            {
                for (size_t i = 0; i < m_routePath.size() - 1; i++)
                    stream << m_routePath[i] << ' ';
                stream << m_routePath.back();
            }
            stream << "</path>";
            stream << "</package>";
        FinishWriting
        return pStream;
    }

    std::string CConnectionInfoPackage::ToText(void) const
    {
        if (m_connectionStatus == Connected)
        {
            if (m_sourceIdentity == Server)
                return "ConnectionInfo: Server: " + m_serverHostString;
            else
                return Poco::format("ConnectionInfo: Client: %s(%s)", m_studentName, m_studentNumber);
        }
        else
        {
            if (m_sourceIdentity == Server)
                return "Server: " + m_serverHostString + " disconnected";
            else
                return Poco::format("Client: %s(%s) disconnected", m_studentName, m_studentNumber);
        }
    }


    /**************************************************************************\
        question package
    \**************************************************************************/
    const std::string CQuestionPackage::questionTypeName[] = {
        "choice", "filling", "typing", "qna", "office", "coding", "html", "photoshop"
    };
    CQuestionPackage::CQuestionPackage(void)
    {

    }

    void CQuestionPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == labelPackage)
        {
            const std::string &questionType = attributes.getValue(namespaceURI, "questionType");
            int idx = 0;
            for (; idx < QuestionTypeCount; idx++)
            {
                if (questionTypeName[idx] == questionType)
                    break;
            }

            m_question = QuestionCreator(static_cast<QuestionType>(idx));
            if (m_question)
            {
                const std::string &id = attributes.getValue(namespaceURI, "id");
                std::stringstream sstr(id);
                sstr >> m_question->questionID;
            }
        }
        else if (m_question && localName == "option")
        {
            static_cast<CChoiceQuestion *>(m_question.get())->questionOptions.push_back("");
        }
        else if (m_question && localName == "typing")
        {
            const std::string &time = attributes.getValue(namespaceURI, "time");
            std::stringstream sstr(time);
            sstr >> static_cast<CTypingQuestion *>(m_question.get())->questionTime;
            const std::string &chinese = attributes.getValue(namespaceURI, "chinese");
            sstr.str(chinese);
            sstr.clear();
            sstr >> static_cast<CTypingQuestion *>(m_question.get())->flagChineseTyping;
        }
        else if (m_question && localName == "filter")
        {
            CFiledQuestion &q = DynamicCast<CFiledQuestion>(m_question);    
            q.answerFiles.push_back(attributes.getValue(namespaceURI, "pattern"));
        }
        else if (m_question && localName == "attached")
        {
            CFiledQuestion &q = DynamicCast<CFiledQuestion>(m_question);
            std::stringstream sstr(attributes.getValue(namespaceURI, "start"));
            sstr >> q.questionStartFileIdx;
        }

        m_currentLabel = localName;
        m_receivedString.clear();
    }

    void CQuestionPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (m_currentLabel == "attached")
        {
            CFiledQuestion &q = DynamicCast<CFiledQuestion>(m_question);
            q.questionFile.SetEncodedString(m_receivedString);
        }
        m_currentLabel.clear();
        m_receivedString.clear();
    }

    void CQuestionPackage::characters(const XMLChar ch[], int start, int length)
    {
        if (!m_question)
            return;

        if (m_currentLabel == "title")
            m_question->questionTitle += std::string(ch + start, length);
        else if (m_currentLabel == "option")
        {
            static_cast<CChoiceQuestion *>(m_question.get())->questionOptions.back() 
                += std::string(ch + start, length);
        }
        else
            m_receivedString += std::string(ch + start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CQuestionPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting;
            if (!m_question)
            {
                stream << Poco::format(
                    "<package sid=\"%u\" type=\"question\" questionType=\"None\" />",
                    systemID
                    );
            }
            else
            {
                stream << Poco::format(
                    "<package sid=\"%u\" type=\"%s\" questionType=\"%s\" id=\"%d\">",
                    systemID, std::string("question"), questionTypeName[m_question->Type()], m_question->questionID
                    );
                stream << "<title>" << m_question->questionTitle << "</title>";
                switch (m_question->Type())
                {
                case typeChoice:
                    {
                        CChoiceQuestion &q = StaticCast<CChoiceQuestion>(m_question);
                        for (size_t i = 0; i < q.questionOptions.size(); i++)
                            stream << "<option>" << q.questionOptions[i] << "</option>";
                        break;
                    }
                case typeFilling:
                    break;
                case typeTyping:
                    {
                        CTypingQuestion &q = StaticCast<CTypingQuestion>(m_question);
                        stream << Poco::format("<typing time=\"%u\" chinese=\"%u\" />",
                            q.questionTime, q.flagChineseTyping);
                        break;
                    }
                case typeQnA:
                    break;
                case typeOffice:
                case typeCoding:
                case typeHtml:
                case typePhotoshop:
                    {
                    const CFiledQuestion &q = DynamicCast<CFiledQuestion>(m_question);
                    for (size_t i = 0; i < q.answerFiles.size(); i++)
                        stream << "<filter pattern=\"" << q.answerFiles[i] << "\" />";
                    stream << "<attached start=\""<<q.questionStartFileIdx<<"\">" << q.questionFile.GetEncodedString() << "</attached>";
                    break;
                    }
                }
                stream << "</package>";
            }
        FinishWriting;
        return pStream;
    }

    std::string CQuestionPackage::ToText(void) const
    {
        if (!m_question)
            return "All questions have been sent!";

        std::stringstream sstr;
        sstr << std::endl;
        sstr << questionTypeName[m_question->Type()] << "[" << m_question->questionID << "]" << ":"<< std::endl;
        switch (m_question->Type())
        {
        case typeChoice:
            {
                const CChoiceQuestion &q = StaticCast<CChoiceQuestion>(m_question);
                for (size_t i = 0; i < q.questionOptions.size(); i++)
                    sstr << "option " << i << ": " << q.questionOptions[i] << std::endl;
                break;
            }
        case typeFilling:
            break;
        case typeTyping:
            {
                const CTypingQuestion &q = StaticCast<CTypingQuestion>(m_question);
                sstr << Poco::format("time: %u", q.questionTime) << std::endl;
                if (q.flagChineseTyping)
                    sstr << "Chinese typing" << std::endl;
                break;
            }
        case typeQnA:
            break;
        case typeOffice:
        case typeCoding:
        case typeHtml:
        case typePhotoshop:
            {
                break;
            }
        }
        return sstr.str();
    }

    bool CQuestionPackage::IsSucceeded(void) const
    {
        return true;
    }

    /**************************************************************************\
        question query package
    \**************************************************************************/
    void CQuestionQueryPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
    }

    SharedPtr<SafeSocketOutputStream> & CQuestionQueryPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting;
        stream << Poco::format(
            "<package sid=\"%u\" type=\"%s\" />", systemID, std::string("questionQuery")
            );
        FinishWriting;
        return pStream;
    }

    std::string CQuestionQueryPackage::ToText(void) const
    {
        return "Question query";
    }


    /**************************************************************************\
        criterion package
    \**************************************************************************/
    void CCriterionPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == labelPackage)
        {
            const std::string &questionType = attributes.getValue(namespaceURI, "questionType");
            int idx = 0;
            for (; idx < QuestionTypeCount; idx++)
            {
                if (CQuestionPackage::questionTypeName[idx] == questionType)
                    break;
            }

            m_criterion = CriterionCreator(static_cast<QuestionType>(idx));
            if (m_criterion)
            {
                const std::string &id = attributes.getValue(namespaceURI, "id");
                std::stringstream sstr(id);
                sstr >> m_criterion->questionID;
            }
        }
        else if (m_criterion && localName == "choice")
        {
            const std::string &criterionChoice = attributes.getValue(namespaceURI, "criterionChoice");
            std::stringstream sstr(criterionChoice);
            sstr >> static_cast<CChoiceCriterion *>(m_criterion.get())->criterionChoice;
        }
        else if (m_criterion && localName == "blank")
        {
            static_cast<CFillingCriterion *>(m_criterion.get())->critierionBlanks.push_back("");
        }
        else if (m_criterion && localName == "property")
        {
            CPropertyCriterion &c = StaticCast<CPropertyCriterion>(m_criterion);
            c.criterionProperties.push_back(attributes.getValue(namespaceURI, "name"));
            c.criterionValues.push_back(attributes.getValue(namespaceURI, "value"));
            std::stringstream sstr(attributes.getValue(namespaceURI, "weight"));
            double w = 1.0;
            sstr >> w;
            c.criterionWeights.push_back(w);
        }

        m_currentLabel = localName;
        m_receivedString.clear();
    }

    void CCriterionPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (m_criterion && localName == "files")
        {
            CFiledCriterion &c = StaticCast<CFiledCriterion>(m_criterion);
            c.criterionFile.SetEncodedString(m_receivedString);
        }
        m_currentLabel.clear();
        m_receivedString.clear();
    }

    void CCriterionPackage::characters(const XMLChar ch[], int start, int length)
    {
        if (!m_criterion)
            return;

        m_receivedString += std::string(ch + start, length);

        if (m_currentLabel == "blank")
        {
            static_cast<CFillingCriterion *>(m_criterion.get())->critierionBlanks.back() 
                += std::string(ch + start, length);
        }
    }

    SharedPtr<SafeSocketOutputStream> & CCriterionPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting;
        if (!m_criterion)
        {
            stream << Poco::format(
                "<package sid=\"%u\" type=\"criterion\" questionType=\"None\" />",
                systemID
                );
        }
        else
        {
            stream << Poco::format(
                "<package sid=\"%u\" type=\"%s\" questionType=\"%s\" id=\"%d\">",
                systemID, std::string("criterion"), CQuestionPackage::questionTypeName[m_criterion->Type()], m_criterion->questionID
                );
            switch (m_criterion->Type())
            {
            case typeChoice:
                {
                    CChoiceCriterion &c = StaticCast<CChoiceCriterion>(m_criterion);
                    stream << Poco::format("<choice criterionChoice=\"%d\" />", c.criterionChoice);
                    break;
                }
            case typeFilling:
                {
                    CFillingCriterion &c = StaticCast<CFillingCriterion>(m_criterion);
                    for (size_t i = 0; i < c.critierionBlanks.size(); i++)
                    {
                        stream << "<blank>" << c.critierionBlanks[i] << "</blank>";
                    }
                    break;
                }
            case typeTyping:
                break;
            case typeQnA:
                break;
            case typeOffice:
            case typeHtml:
                {
                    CPropertyCriterion &c = StaticCast<CPropertyCriterion>(m_criterion);
                    for (size_t i = 0; i < c.criterionProperties.size(); i++)
                    {
                        stream << Poco::format("<property name=\"%s\" value=\"%s\" weight=\"%0.3f\" />",
                            c.criterionProperties[i],
                            c.criterionValues[i],
                            c.criterionWeights[i]
                        );
                    }
                    break;
                }
            case typeCoding:
                {
                    CFiledCriterion &c = StaticCast<CFiledCriterion>(m_criterion);
                    stream << "<files>" << c.criterionFile.GetEncodedString() << "</files>";
                    break;
                }
            case typePhotoshop:
                break;
            }
            stream << "</package>";
        }
        FinishWriting;
        return pStream;
    }

    std::string CCriterionPackage::ToText(void) const
    {
        if (!m_criterion)
            return "All criterions have been sent!";

        std::stringstream sstr;
        sstr << std::endl;
        sstr << CQuestionPackage::questionTypeName[m_criterion->Type()]
            << "[" << m_criterion->questionID << "]" << std::endl;
        switch (m_criterion->Type())
        {
        case typeChoice:
            {
                const CChoiceCriterion &c = StaticCast<CChoiceCriterion>(m_criterion);
                sstr << Poco::format("criterionChoice: %d", c.criterionChoice);
                break;
            }
        case typeFilling:
            {
                const CFillingCriterion &c = StaticCast<CFillingCriterion>(m_criterion);
                for (size_t i = 0; i < c.critierionBlanks.size(); i++)
                {
                    sstr << "blank " << i << ": " << c.critierionBlanks[i] << std::endl;
                }
                break;
            }
        case typeTyping:
            break;
        case typeQnA:
            break;
        case typeOffice:
        case typeHtml:
            break;
        case typeCoding:
            break;
        case typePhotoshop:
            break;
        }
        return sstr.str();
    }

    bool CCriterionPackage::IsSucceeded(void) const
    {
        return true;
    }

    /**************************************************************************\
        criterion query package
    \**************************************************************************/
    void CCriterionQueryPackage::startElement(const XMLString& uri, const XMLString& localName, const XMLString& qname, const Attributes& attributes)
    {
        ParseID;
        if (localName == labelPackage)
        {
            for (int i = 0; i < QuestionTypeCount; i++)
                m_questionIds[i].clear();
        }
        else if (localName == "question")
        {          
            const std::string &type = attributes.getValue(namespaceURI, "type");
            int idx = 0;
            for (; idx < QuestionTypeCount; idx++)
            {
                if (CQuestionPackage::questionTypeName[idx] == type)
                    break;
            }

            m_currentType = static_cast<QuestionType>(idx);
            const std::string &count = attributes.getValue(namespaceURI, "count");
            std::stringstream sstr(count);
            sstr >> idx;
            if (m_currentType != QuestionTypeCount)
            {
                m_questionIds[m_currentType].resize(idx);
            }
        }
        m_currentLabel = localName;
    }

    void CCriterionQueryPackage::endElement(const XMLString& uri, const XMLString& localName, const XMLString& qname)
    {
        if (localName == "question" && m_currentType != QuestionTypeCount)
        {
            std::stringstream sstr(m_receivingString);
            for (size_t i = 0; i < m_questionIds[m_currentType].size(); i++)
                sstr >> m_questionIds[m_currentType][i];
        }
        m_currentLabel.clear();
        m_receivingString.clear();
    }

    void CCriterionQueryPackage::characters(const XMLChar ch[], int start, int length)
    {
        if (m_currentLabel == "question")
            m_receivingString += std::string(ch + start, length);
    }

    SharedPtr<SafeSocketOutputStream> & CCriterionQueryPackage::WriteToStream(SharedPtr<SafeSocketOutputStream> &pStream)
    {
        StartWriting;
        stream << Poco::format(
            "<package sid=\"%u\" type=\"%s\">",
            systemID, std::string("criterionQuery")
            );
        for (int i = 0; i < QuestionTypeCount; i++)
        {
            stream << "<question type=\"" << CQuestionPackage::questionTypeName[i] << "\" "
                << "count=\"" << m_questionIds[i].size() << "\">";

            for (size_t j = 0; j < m_questionIds[i].size(); j++)
                stream << m_questionIds[i][j] << " ";

            stream << "</question>";
        }

        stream << "</package>";

        FinishWriting;
        return pStream;
    }

    std::string CCriterionQueryPackage::ToText(void) const
    {
        std::stringstream sstr;
        sstr << "Criterion Query:" << std::endl;
        for (int i = 0; i < QuestionTypeCount; i++)
        {
            sstr << '\t';
            sstr << CQuestionPackage::questionTypeName[i] << ": ";

            for (size_t j = 0; j < m_questionIds[i].size(); j++)
                sstr << m_questionIds[i][j] << " ";

            sstr << std::endl;
        }
        return sstr.str();
    }

}